#include <stdint.h> // 定义了扩展的整数类型和宏
#include <stdio.h>  // standard input output 标准输入输出函数
#include <stdlib.h> // standard library 标准库函数头文件

#include <fcntl.h>  // unix标准中通用的头文件 define O_WRONLY and O_RDONLY
#include <unistd.h> // POSIX 系统 API 访问功能的头文件

#include "hilog_wrapper.h"
#include "napi/native_api.h"
#include "napi/native_node_api.h"

#define GPIO_VAL_LOW 0
#define GPIO_VAL_HIGH 1

#define log(fmt, ...) HILOG_INFO("mynapidemo#" fmt, ##__VA_ARGS__)

// "add"接口C/C++实现代码
static napi_value add(napi_env env, napi_callback_info info) {
  log("%{public}s,called", __func__);
  // 1. 获取JS传入的参数
  size_t argc = 2;
  napi_value args[2] = {nullptr};
  NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr));

  // 2. 验证参数
  // 2.1 验证参数个数
  NAPI_ASSERT(env, argc >= 2, "Wrong number of arguments");

  // 2.2 验证参数数据类型
  napi_valuetype valuetype0;
  NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
  napi_valuetype valuetype1;
  NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));
  NAPI_ASSERT(env, valuetype0 == napi_number && valuetype1 == napi_number, "Wrong argument type. Numbers expected.");

  // 3. NAPI类型数据转为C/C++类型
  double value0;
  NAPI_CALL(env, napi_get_value_double(env, args[0], &value0));
  double value1;
  NAPI_CALL(env, napi_get_value_double(env, args[1], &value1));

  // 4. 业务逻辑计算
  double sum = value0 + value1;

  // 5. 将结果由C/C++转为NAPI类型
  napi_value ret;
  NAPI_CALL(env, napi_create_double(env, sum, &ret));
  return ret;
}

struct LedAddOnData {
  napi_async_work asyncWork = nullptr; // 异步工作项
  napi_deferred deferred = nullptr;    // 用于Promise的resolve、reject
  napi_ref callback = nullptr;         // 回调函数
  int args[2] = {0};                   // 2个输入参数
  int result = 0;                      // 业务逻辑处理结果（返回值）
};

// 业务逻辑处理函数，由worker线程池调度执行。
static void executeCB(napi_env env, void *data) {
  LedAddOnData *addonData = (LedAddOnData *)data;
  // Hi3516DV300开发板
  // char direction[100] = {0};
  // sprintf(direction, "echo out > /sys/class/gpio/gpio%d/direction", addonData->args[0]);
  // system(direction);
  // char value[100] = {0};
  // sprintf(value, "echo %d > /sys/class/gpio/gpio%d/value", addonData->args[1], addonData->args[0]);
  // addonData->result = system(value);
  
  // rk3568开发板
  const char *ledColor = addonData->args[0] == 146 ? "blue" : (addonData->args[0] == 147 ? "red" : "green");
  if (addonData->args[1] == 0) { // light off
    char brightness[100] = {0};
    sprintf(brightness, "echo %d > /sys/class/leds/%s/brightness", addonData->args[1], ledColor);
    addonData->result = system(brightness);
  } else { // light on(blink)
    char trigger[100] = {0};
    sprintf(trigger, "echo timer > /sys/class/leds/%s/trigger", ledColor);
    addonData->result = system(trigger);
  }
}

// 业务逻辑处理完成回调函数，在业务逻辑处理函数执行完成或取消后触发，由EventLoop线程中执行。
static void completeCBForCallback(napi_env env, napi_status status, void *data) {
  LedAddOnData *addonData = (LedAddOnData *)data;
  napi_value callback = nullptr;
  napi_get_reference_value(env, addonData->callback, &callback);
  napi_value undefined = nullptr;
  napi_get_undefined(env, &undefined);
  napi_value result = nullptr;
  napi_create_int32(env, addonData->result, &result);

  // 执行回调函数
  napi_value returnVal = nullptr;
  napi_call_function(env, undefined, callback, 1, &result, &returnVal);

  // 删除napi_ref对象
  if (addonData->callback != nullptr) {
    napi_delete_reference(env, addonData->callback);
  }

  // 删除异步工作项
  napi_delete_async_work(env, addonData->asyncWork);
  delete addonData;
}

static napi_value setLedStatusWithCallback(napi_env env, napi_callback_info info) {
  // 获取3个参数，值的类型是js类型（napi_value）
  size_t argc = 3;
  napi_value args[3];
  napi_value thisArg = nullptr;
  NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr));

  // 异步工作项上下文用户数据，传递到异步工作项的execute、complete中传递数据
  auto addonData = new LedAddOnData{
      .asyncWork = nullptr,
  };

  // 将接收到的参数传入用户自定义上下文数据
  NAPI_CALL(env, napi_get_value_int32(env, args[0], &addonData->args[0]));
  NAPI_CALL(env, napi_get_value_int32(env, args[1], &addonData->args[1]));
  NAPI_CALL(env, napi_create_reference(env, args[2], 1, &addonData->callback));

  // 创建async work，创建成功后通过最后一个参数接收async work的handle
  napi_value resourceName = nullptr;
  napi_create_string_utf8(env, "setLedStatusWithCallback", NAPI_AUTO_LENGTH, &resourceName);
  napi_create_async_work(env, nullptr, resourceName, executeCB, completeCBForCallback, (void *)addonData,
                         &addonData->asyncWork);

  // 将刚创建的async work加到队列，由底层去调度执行x
  napi_queue_async_work(env, addonData->asyncWork);

  // 原生方法返回空对象
  napi_value result = 0;
  NAPI_CALL(env, napi_get_null(env, &result));
  return result;
}

static void completeCBForPromise(napi_env env, napi_status status, void *data) {
  LedAddOnData *addonData = (LedAddOnData *)data;
  napi_value result = nullptr;
  napi_create_int32(env, addonData->result, &result);
  napi_resolve_deferred(env, addonData->deferred, result);

  // 删除napi_ref对象
  if (addonData->callback != nullptr) {
    napi_delete_reference(env, addonData->callback);
  }

  // 删除异步工作项
  napi_delete_async_work(env, addonData->asyncWork);
  delete addonData;
}

static napi_value setLedStatusWithPromise(napi_env env, napi_callback_info info) {
  // 获取2个参数，值的类型是js类型（napi_value）
  size_t argc = 2;
  napi_value args[2];
  napi_value thisArg = nullptr;
  NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr));

  // 创建promise
  napi_value promise = nullptr;
  napi_deferred deferred = nullptr;
  NAPI_CALL(env, napi_create_promise(env, &deferred, &promise));

  // 异步工作项上下文用户数据，传递到异步工作项的execute、complete之间传递数据
  auto addonData = new LedAddOnData{
      .asyncWork = nullptr,
      .deferred = deferred,
  };

  // 将接收到的参数传入
  NAPI_CALL(env, napi_get_value_int32(env, args[0], &addonData->args[0]));
  NAPI_CALL(env, napi_get_value_int32(env, args[1], &addonData->args[1]));

  // 创建async work，创建成功后通过最后一个参数(addonData->asyncWork)返回asyncwork的handle
  napi_value resourceName = nullptr;
  napi_create_string_utf8(env, "setLedStatusWithPromise", NAPI_AUTO_LENGTH, &resourceName);
  napi_create_async_work(env, nullptr, resourceName, executeCB, completeCBForPromise, (void *)addonData,
                         &addonData->asyncWork);

  // 将刚创建的async work加到队列，由底层去调度执行
  napi_queue_async_work(env, addonData->asyncWork);

  // 返回promise
  return promise;
}

static napi_value setLedStatus(napi_env env, napi_callback_info info) {
  log("%{public}s,called", __func__);
  // 获取3个参数，值的类型是js类型（napi_value）
  size_t argc = 3;
  napi_value args[3];
  napi_value thisArg = nullptr;
  NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr));

  // 获取并判断js参数类型
  napi_valuetype valuetype0;
  NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
  napi_valuetype valuetype1;
  NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));
  if (valuetype0 != napi_number || valuetype1 != napi_number) {
    napi_throw_type_error(env, nullptr, "Wrong arguments. 2 numbers expected.");
    return NULL;
  }

  // 异步工作项上下文用户数据，传递到异步工作项的execute、complete中传递数据
  auto addonData = new LedAddOnData{
      .asyncWork = nullptr,
  };

  if (argc == 2) {
    // 创建promise
    napi_value promise = nullptr;
    napi_deferred deferred = nullptr;
    NAPI_CALL(env, napi_create_promise(env, &deferred, &promise));
    addonData->deferred = deferred;

    // 将接收到的参数传入
    NAPI_CALL(env, napi_get_value_int32(env, args[0], &addonData->args[0]));
    NAPI_CALL(env, napi_get_value_int32(env, args[1], &addonData->args[1]));

    // 创建async work，创建成功后通过最后一个参数(addonData->asyncWork)返回async work的handle
    napi_value resourceName = nullptr;
    napi_create_string_utf8(env, "setLedStatusPromise", NAPI_AUTO_LENGTH, &resourceName);
    napi_create_async_work(env, nullptr, resourceName, executeCB, completeCBForPromise, (void *)addonData,
                           &addonData->asyncWork);

    // 将刚创建的async work加到队列，由底层去调度执行
    napi_queue_async_work(env, addonData->asyncWork);

    // 返回promise
    return promise;
  } else {
    napi_valuetype valuetype2;
    NAPI_CALL(env, napi_typeof(env, args[2], &valuetype2));
    if (valuetype2 != napi_function) {
      napi_throw_type_error(env, nullptr, "Callback function expected.");
      return NULL;
    }

    // 将接收到的参数传入用户自定义上下文数据
    NAPI_CALL(env, napi_get_value_int32(env, args[0], &addonData->args[0]));
    NAPI_CALL(env, napi_get_value_int32(env, args[1], &addonData->args[1]));
    NAPI_CALL(env, napi_create_reference(env, args[2], 1, &addonData->callback));

    // 创建async work，创建成功后通过最后一个参数接收async work的handle
    napi_value resourceName = nullptr;
    napi_create_string_utf8(env, "setLedStatusCallback", NAPI_AUTO_LENGTH, &resourceName);
    napi_create_async_work(env, nullptr, resourceName, executeCB, completeCBForCallback, (void *)addonData,
                           &addonData->asyncWork);

    // 将刚创建的async work加到队列，由底层去调度执行
    napi_queue_async_work(env, addonData->asyncWork);

    // 原生方法返回空对象
    napi_value result = 0;
    NAPI_CALL(env, napi_get_null(env, &result));
    return result;
  }
}

// 注册接口
static napi_value registerMyNapiDemoApis(napi_env env, napi_value exports) {
  napi_value gpioValHigh = gpioValHigh;
  napi_value gpioValLow = nullptr;
  napi_create_int32(env, GPIO_VAL_HIGH, &gpioValHigh);
  napi_create_int32(env, GPIO_VAL_LOW, &gpioValLow);
  static napi_property_descriptor desc[] = {
      DECLARE_NAPI_FUNCTION("add", add),
      DECLARE_NAPI_FUNCTION("setLedStatusWithCallback", setLedStatusWithCallback),
      DECLARE_NAPI_FUNCTION("setLedStatusWithPromise", setLedStatusWithPromise),
      DECLARE_NAPI_FUNCTION("setLedStatus", setLedStatus),
      DECLARE_NAPI_STATIC_PROPERTY("LED_ON", gpioValHigh),
      DECLARE_NAPI_STATIC_PROPERTY("LED_OFF", gpioValLow),
  };
  NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
  return exports;
}

/*
 * 模块定义
 */
static napi_module myNapiDemoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = registerMyNapiDemoApis,
    .nm_modname = "mynapidemo",
    .nm_priv = ((void *)0),
    .reserved = {0},
};

/*
 * 注册模块
 */
extern "C" __attribute__((constructor)) void RegisterMyNapiDemoModule(void) { napi_module_register(&myNapiDemoModule); }